extern "C" {
	#include <pthread.h>
	#include <unistd.h>

#if __APPLE__
	#include <mach/task.h>
	#include <mach/mach.h>
#endif

#if GLF_PLATFORM_PS3
	#include <sys/timer.h>
	#include <sys/ppu_thread.h>
#endif
}

namespace glf {

#if __APPLE__
struct SchedulingMapping {
	Thread::SchedulingPolicy mode;
	int policy;
} gSchedulingMapping[] = {
	{ Thread::SCHEDULINGPOLICY_FIFO, SCHED_FIFO },
	{ Thread::SCHEDULINGPOLICY_ROUNDROBIN, SCHED_RR },
	{ Thread::SCHEDULINGPOLICY_OTHER, SCHED_OTHER },
};
#else
struct SchedulingMapping {
	Thread::SchedulingPolicy mode;
	int policy;
} gSchedulingMapping[] = {
	{ Thread::SCHEDULINGPOLICY_FIFO, SCHED_FIFO },
	{ Thread::SCHEDULINGPOLICY_ROUNDROBIN, SCHED_RR },
};
#endif

Thread::Features Thread::GetFeatures() {
	return FEATURE_MAX_THREADS | FEATURE_MAIN_CAN_SLEEP | FEATURE_CAN_SUSPEND_RESUME;
}
#if GLF_AS_DLL
const int THREAD_PRIORITY_MIN = 64;
const int THREAD_PRIORITY_NORM = 31;
const int THREAD_PRIORITY_MAX = 0;
#else
const int Thread::PRIORITY_MIN = 64;
const int Thread::PRIORITY_NORM = 31;
const int Thread::PRIORITY_MAX = 0;

#endif
struct Thread::Impl : public thread::ImplBase {

	pthread_t mPThread;
	bool mIsMain;

	Impl(Thread& thread) :
		ImplBase(thread),
		mPThread(0),
		mIsMain(false) {

	}

	Impl(Thread& thread, bool isMain) :
		ImplBase(thread),
		mPThread(pthread_self()),
		mIsMain(isMain) {

#if !GLF_PLATFORM_NACL
		sched_param param;
		int policy;

		pthread_getschedparam (mPThread, &policy, &param);

		mThread.mPriority = param.sched_priority;
#else
        mThread.mPriority = Thread::GetPriorityNorm();
#endif
	}

	~Impl() {
	}

	void Join() {
		if(mPThread)
		{
			pthread_join(mPThread, 0);
			mPThread = 0;
		}
	}

	bool IsSchedulingSupported(Thread::SchedulingPolicy policy) const {
		for(size_t i = 0; i < GLF_ASIZEOF(gSchedulingMapping); ++i) {
			if(gSchedulingMapping[i].policy == policy)
				return true;
		}
		return false;
	}

	static void* RunThread(void* ud) {
		Impl* impl = static_cast<Impl*>(ud);

		Thread::MyRun(impl->mThread);

		return 0;
	}

	int GetPriority() const {
		sched_param param;
		int schedMode;
		pthread_getschedparam(mPThread, &schedMode, &param);
		return param.sched_priority;
	}

#if __APPLE__
	static int GetNumberOfThreads() {
		task_t self = mach_task_self();
		thread_t self_thr = mach_thread_self();
		thread_act_array_t threads;
		mach_msg_type_number_t number;
		if(task_threads(self, &threads, &number) != KERN_SUCCESS) {
			return 0;
		}
		vm_deallocate(mach_task_self(), (vm_address_t)threads, number * sizeof(thread_act_t));
		return number;
	}

	void SetName(const stringc name) {
		if(IsCurrent()) {
			pthread_setname_np(name.c_str());
		} else {
			printf("[glf] Thread::SetName called on non-current thread\n");
		}

		mName = name;
	}

	void UpdateName() {
		char buffer[256];
		pthread_getname_np(mPThread, buffer, sizeof(buffer));
		mName = buffer;
	}

	void Suspend() {
		mach_port_t mp = pthread_mach_thread_np(mPThread);
		thread_suspend(mp);
		
	}
	void Resume() {
		mach_port_t mp = pthread_mach_thread_np(mPThread);
		thread_resume(mp);
	}
#endif

	void SetPriority(int npriority) {
#if GLF_PLATFORM_IPHONE
		sched_param param;
		int schedMode;
		pthread_getschedparam(mPThread, &schedMode, &param);

		int minprio = sched_get_priority_min(schedMode);
		int maxprio = sched_get_priority_max(schedMode);

		mThread.mPriority = param.sched_priority = Clamp(npriority, minprio, maxprio);
		pthread_setschedparam(mPThread, schedMode, &param);

#else
		//pthread_setschedprio(mPThread, npriority);
		mThread.mPriority = npriority;
#endif

	}

	Thread::SchedulingPolicy GetSchedulingPolicy()
	{
		sched_param param;
		int policy;
		pthread_getschedparam(mPThread, &policy, &param);

		for(size_t i = 0; i < GLF_ASIZEOF(gSchedulingMapping); ++i) {
			const SchedulingMapping& sm = gSchedulingMapping[i];
			if(sm.policy == policy)
				return sm.mode;
		}
		
		return SCHEDULINGPOLICY_UNKNOWN;
	}

	void SetSchedulingPolicy(SchedulingPolicy mode)
	{
		for(size_t i = 0; i < GLF_ASIZEOF(gSchedulingMapping); ++i) {
			const SchedulingMapping& sm = gSchedulingMapping[i];
			if(sm.mode == mode) {
				sched_param param;
				int policy;
				pthread_getschedparam(mPThread, &policy, &param);
				pthread_setschedparam(mPThread, sm.policy, &param);
				break;
			}
		}
	}

	bool Start(int priority, uint stackSize) {
		GLFi_ASSERT(mPThread == 0);

		pthread_attr_t attr;
		pthread_attr_init(&attr);
		pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
		pthread_attr_setstacksize(&attr, stackSize);

		Impl& mainThreadImpl = GetGlobals()->threadMgr.mMainThread.GetImpl();

		bool isOk = pthread_create(&mPThread, &attr, RunThread, this) == 0;

		if(isOk) {
			SetPriority(priority);
		}

		pthread_attr_destroy(&attr);

		return isOk;
	}
	bool IsCurrent() const {
		bool isCurrent = pthread_equal(pthread_self(), mPThread) != 0;

		return isCurrent;
	}
	bool IsMain() const {
		return mIsMain;
	}
	static void Yield() {
#if GLF_PLATFORM_IPHONE || defined (GLF_PLATFORM_MACOSX)
		sched_yield();	//pthread_yield_np(); ?
#elif GLF_PLATFORM_ANDROID || GLF_PLATFORM_NACL
		sched_yield();
#elif GLF_PLATFORM_PS3
        sys_ppu_thread_yield();
#elif GLF_PLATFORM_PLAYBOOK
    	struct timespec ts = {0,0};
    	nanosleep(&ts, NULL);
#else
		pthread_yield();
#endif
	}
	static void Sleep(uint ms) {
#if GLF_PLATFORM_PS3
		sys_timer_usleep(ms*1000);
#else
		if(ms == 0)
		{
			::usleep(1);
		}
		else
		{
			::usleep(ms*1000);
		}
#endif
	}
};
} //namespace glf
